﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Media.Media3D;
using System.Windows.Threading;
using System.Windows.Controls;
using System.Threading;

namespace FlyingCell
{
    class UserControl3DGroup : UserControl3D
    {
        /// <summary>
        /// The list of UserControl3D in the group
        /// </summary>
        private List<UserControl3D> _mList = null;


        /// <summary>
        /// UserControl3DGroup position
        /// </summary>
        public override Point3D mPosition
        {
            get
            {
                return _mPosition;
            }

            set
            {
                if (_mList != null)
                {
                    Vector3D Delta = value - _mPosition;

                    foreach (UserControl3D UCIndex in _mList)
                    {
                        UCIndex.mPosition += Delta;
                    }
                }
                _mPosition = value;
            }
        }
        private Point3D _mPosition;

        /// <summary>
        /// UserControl3DGroup selection state
        /// </summary>
        private bool IsGroupSelected = false;

        /// <summary>
        /// UserControl3D currently selected in the group
        /// </summary>
        private UserControl3D SelectedUC = null;

        /// <summary>
        /// Default constructor
        /// </summary>
        public UserControl3DGroup()
        {
            _mList = new List<UserControl3D>();
        }

        /// <summary>
        /// Constructor
        /// </summary>
        public UserControl3DGroup(string Name)
        {
            mName = Name;

            _mList = new List<UserControl3D>();
        }

        /// <summary>
        /// Add a UserControl3D in the group
        /// </summary>
        /// <param name="UserControl">The userControl3D to be added</param>
        public void Add(UserControl3D UserControl)
        {
            _mList.Add(UserControl);
        }

        /// <summary>
        /// Insert a UserControl3D at index
        /// </summary>
        /// <param name="Index">The Index</param>
        /// <param name="UserControl">Teh UserControl3D</param>
        public void Insert(int Index, UserControl3D UserControl)
        {
            _mList.Insert(Index, UserControl);
        }

        /// <summary>
        /// Remove the first occurence of the UserControl3D from the group
        /// </summary>
        /// <param name="UserControl">The UserControl3D to be removed</param>
        /// <returns>true if found, false else</returns>
        public bool Remove(UserControl3D UserControl)
        {
            return _mList.Remove(UserControl);
        }

        /// <summary>
        /// Clear the group
        /// </summary>
        public void RemoveAll()
        {
            foreach (UserControl3D UCIndex in _mList)
            {
                _mList.Remove(UCIndex);
            }
        }

        /// <summary>
        /// Remove a UserControl3D at index
        /// </summary>
        /// <param name="Index">The index</param>
        public void RemoveAt(int Index)
        {
            _mList.RemoveAt(Index);
        }

        /// <summary>
        /// Get a UserControl3D by index in the group
        /// </summary>
        /// <param name="Index">Zero based index (return SelectedUC if negative)</param>
        /// <returns>The UserControl3D or null if not found</returns>
        public UserControl3D Get(int Index)
        {
            if (Index < 0)
            {
                return SelectedUC;
            }

            if (Index < _mList.Count)
            {
                return _mList[Index];
            }

            return null;
        }

        /// <summary>
        /// The number of UserControl3D in the group
        /// </summary>
        /// <returns>The number</returns>
        public int Count()
        {
            return _mList.Count;
        }

        /// <summary>
        /// Update the content of the group
        /// </summary>
        public override void StartUpdateContent(DispatcherObject Disp)
        {
            // TODO: To be verified!
            UpdateContentCounter = 0;
            foreach (UserControl3D UCIndex in _mList)
            {
                UCIndex.UpdateContentCompleted += UpdateContent_Completed;
                UCIndex.StartUpdateContent(Disp);
                UpdateContentCounter++;
            }
        }

        /// <summary>
        /// Update complete event on which external class can register a handler
        /// </summary>
        public override event EventHandler<UserControl3DStatusReport> UpdateContentCompleted;

        /// <summary>
        /// Count down the completed updates
        /// </summary>
        private int UpdateContentCounter = 0;

        /// <summary>
        /// Raise Update completed event
        /// </summary>
        private void UpdateContent_Completed(object sender, UserControl3D.UserControl3DStatusReport e)
        {
            // TODO: Protect access to this int
            UpdateContentCounter--;

            (sender as UserControl3D).UpdateContentCompleted -= UpdateContent_Completed;

            if (UpdateContentCounter == 0)
            {
                if (UpdateContentCompleted != null)
                {
                    UserControl3DStatusReport Args = new UserControl3DStatusReport(this);
                    UpdateContentCompleted(this, Args);
                }
            }
        }

        /// <summary>
        /// Apply the bounding box on the object
        /// </summary>
        /// <param name="Box">The bounding box in which UserControl3DGroup is considered (can be null to use the registered one)</param>
        public override void ApplyBoundingBox(BoundingBox Box)
        {
            foreach (UserControl3D UCIndex in _mList)
            {
                UCIndex.ApplyBoundingBox(Box);
            }
        }

        /// <summary>
        /// Cancel the bounding box on the object
        /// </summary>
        public override void CancelBoundingBox()
        {
            foreach (UserControl3D UCIndex in _mList)
            {
                UCIndex.CancelBoundingBox();
            }
        }

        /// <summary>
        /// Add the UserControl3DGroup into the 3D scene
        /// </summary>
        /// <param name="Viewport">the 3D scene (can be null to use the registered one)</param>
        /// <param name="Box">The bounding box in which UserControl3DGroup is considered (can be null to use the registered one)</param>
        public override void AddTo3DSceneWithBounding(Viewport3D Viewport, BoundingBox Box)
        {
            foreach (UserControl3D UCIndex in _mList)
            {
                UCIndex.AddTo3DSceneWithBounding(Viewport, Box);
            }
        }

        /// <summary>
        /// Add the UserControl3DGroup the 3D scene
        /// </summary>
        /// <param name="Viewport">the 3D scene (can be null to use the registered one)</param>
        /// <param name="Box">The bounding box in which UserControl3DGroup is considered (can be null to use the registered one)</param>
        public override void AddTo3DScene(Viewport3D Viewport)
        {
            foreach (UserControl3D UCIndex in _mList)
            {
                UCIndex.AddTo3DScene(Viewport);
            }
        }

        /// <summary>
        /// Remove the UserControl3DGroup from the 3D scene
        /// </summary>
        public override void RemoveFrom3DScene()
        {
            foreach (UserControl3D UCIndex in _mList)
            {
                UCIndex.RemoveFrom3DScene();
            }
        }

        /// <summary>
        /// Select the UserControl3DGroup and a UserControl3D in the group.
        /// </summary>
        /// <param name="Index"> Index of the UserControl3D to be selected in the group (-1 if none)</param>
        /// <remarks> 
        /// If you want a specific selection algo for selecting a UserControl3D in the list, you must 
        /// write a method that will input the index into this method.
        /// </remarks>
        public void Select(int Index)
        {
            if (!IsGroupSelected)
            {
                if ((Index >= 0) && (Index < _mList.Count))
                {
                    SelectedUC = _mList[Index];
                    SelectedUC.Select();
                }
                IsGroupSelected = true;
            }
        }

        /// <summary>
        /// Show the UserControl3DGroup as unselected
        /// </summary>
        public override void Unselect()
        {
            if (IsGroupSelected)
            {
                if (SelectedUC != null)
                {
                    SelectedUC.Unselect();
                    SelectedUC = null;
                }

                // To be sure
                foreach (UserControl3D UCIndex in _mList)
                {
                    UCIndex.Unselect();
                }

                IsGroupSelected = false;
            }
        }

        //-------------------------------------------
        // TRANSLATION
        //-------------------------------------------

        /// <summary>
        /// Translate the UserControl3DGroup in X direction
        /// </summary>
        /// <param name="Offset">the length of the translation</param>
        /// <param name="DurationMs">Duration in ms</param>
        public override void StartXTranslation(double Offset, int DurationMs)
        {
            XTranslationAnimationCounter = 0;
            foreach (UserControl3D UCIndex in _mList)
            {
                UCIndex.XTranslationCompleted += XTranslation_Completed;
                UCIndex.StartXTranslation(Offset, DurationMs);
                XTranslationAnimationCounter++;
            }

            // TODO: should not it be in the completion handler???
            _mPosition.X += Offset;
        }

        /// <summary>
        /// Translation complete event on which external class can register a handler
        /// </summary>
        public override event EventHandler<UserControl3DStatusReport> XTranslationCompleted;

        /// <summary>
        /// Count down the completed animations
        /// </summary>
        private int XTranslationAnimationCounter = 0;

        /// <summary>
        /// Translation completed event handler
        /// </summary>
        /// <param name="sender">This</param>
        /// <param name="e">EventArgs</param>
        private void XTranslation_Completed(object sender, UserControl3DStatusReport e)
        {
            // TODO: Protect access to this int
            XTranslationAnimationCounter--;

            (sender as UserControl3D).XTranslationCompleted -= XTranslation_Completed;

            if (XTranslationAnimationCounter == 0)
            {
                // raise event
                if (XTranslationCompleted != null)
                {
                    UserControl3DStatusReport Args = new UserControl3DStatusReport(this);
                    XTranslationCompleted(this, Args);
                }
            }
        }

        /// <summary>
        /// Translate the UserControl3DGroup in Y direction
        /// </summary>
        /// <param name="Offset">the length of the translation</param>
        /// <param name="DurationMs">Duration in ms</param>
        public override void StartYTranslation(double Offset, int DurationMs)
        {
            YTranslationAnimationCounter = 0;
            foreach (UserControl3D UCIndex in _mList)
            {
                UCIndex.YTranslationCompleted += YTranslation_Completed;
                UCIndex.StartYTranslation(Offset, DurationMs);
                YTranslationAnimationCounter++;
            }

            // TODO: should not it be in the completion handler???
            _mPosition.Y += Offset;
        }

        /// <summary>
        /// Translation complete event on which external class can register a handler
        /// </summary>
        public override event EventHandler<UserControl3DStatusReport> YTranslationCompleted;

        /// <summary>
        /// Count down the completed animations
        /// </summary>
        private int YTranslationAnimationCounter = 0;

        /// <summary>
        /// Translation completed event handler
        /// </summary>
        /// <param name="sender">This</param>
        /// <param name="e">EventArgs</param>
        private void YTranslation_Completed(object sender, UserControl3DStatusReport e)
        {
            // TODO: Protect access to this int
            YTranslationAnimationCounter--;

            (sender as UserControl3D).YTranslationCompleted -= YTranslation_Completed;

            if (YTranslationAnimationCounter == 0)
            {
                // raise event
                if (YTranslationCompleted != null)
                {
                    UserControl3DStatusReport Args = new UserControl3DStatusReport(this);
                    YTranslationCompleted(this, Args);
                }
            }
        }

        /// <summary>
        /// Translate the UserControl3DGroup in Z direction
        /// </summary>
        /// <param name="Offset">the length of the translation</param>
        /// <param name="DurationMs">Duration in ms</param>
        public override void StartZTranslation(double Offset, int DurationMs)
        {
            ZTranslationAnimationCounter = 0;
            foreach (UserControl3D UCIndex in _mList)
            {
                UCIndex.ZTranslationCompleted += ZTranslation_Completed;
                UCIndex.StartZTranslation(Offset, DurationMs);
                ZTranslationAnimationCounter++;
            }

            // TODO: should not it be in the completion handler???
            _mPosition.Z += Offset;
        }

        /// <summary>
        /// Translation complete event on which external class can register a handler
        /// </summary>
        public override event EventHandler<UserControl3DStatusReport> ZTranslationCompleted;

        /// <summary>
        /// Count down the completed animations
        /// </summary>
        private int ZTranslationAnimationCounter = 0;

        /// <summary>
        /// Translation completed event handler
        /// </summary>
        /// <param name="sender">This</param>
        /// <param name="e">EventArgs</param>
        private void ZTranslation_Completed(object sender, UserControl3DStatusReport e)
        {
            // TODO: Protect access to this int
            ZTranslationAnimationCounter--;

            (sender as UserControl3D).ZTranslationCompleted -= ZTranslation_Completed;

            if (ZTranslationAnimationCounter == 0)
            {
                // raise event
                if (ZTranslationCompleted != null)
                {
                    UserControl3DStatusReport Args = new UserControl3DStatusReport(this);
                    ZTranslationCompleted(this, Args);
                }
            }
        }

    }
}
